'''
作品名：MinecraftSkinDownloader
Github仓库地址:https://github.com/NewbieXvwu/MinecraftSkinDownloader
Gitee仓库地址:https://gitee.com/NewbieXvwu/MinecraftSkinDownloader
关于本程序：这是一个可以简单地下载任何Minecraft正版玩家的皮肤的软件，使用Python编写，由NewbieXvwu维护。
作者：NewbieXvwu
'''

# 程序版本信息
version_int = 2.7  # 程序主版本号
ispreview = False  # 程序是否是预览版
previewversion = "0"  # 预览版本号（不自动更新）

# 生成字符串版的版本号
if ispreview:
    version = "v" + str(version_int) + " Preview " + previewversion
else:
    version = "v" + str(version_int)

# 导入本地库
import sys
import os
import json
import base64
import tkinter
from urllib.request import urlretrieve
import zipfile
import shutil
from tkinter import messagebox
import threading
import platform
import ctypes
import time

# 尝试导入Windows注册表模块（仅Windows系统可用）
try:
    import winreg
except:
    pass

# 判断是否为Linux系统
def isLinux():
    return platform.architecture()[1] == "ELF"

# 线程控制变量（用于控制安装线程的停止）
global ThreadShouldStop
ThreadShouldStop = False

# 窗口关闭事件处理（安装运行库时防止强制关闭）
def on_closing():
    tkinter.messagebox.showerror("错误", "正在安装必要的运行库！\n强制退出会造成运行库损坏！")

# 尝试安装并导入第三方库（requests）
try:
    import requests
except:  # 未安装requests时执行
    from tkinter import *
    import tkinter
    from tkinter.ttk import *

    # 显示安装进度窗口
    def sc_main_():
        sc_ = Tk()
        # 窗口居中设置
        scw = sc_.winfo_screenwidth()
        sch = sc_.winfo_screenheight()
        w = 300
        h = 200
        x = (scw - w) / 2
        y = (sch - h) / 2
        sc_.title("正在安装运行库")
        sc_.geometry("%dx%d+%d+%d" % (w, h, x, y))
        sc_.maxsize(w, h)
        sc_.minsize(w, h)

        # 尝试设置窗口图标（从本地或网络获取）
        try:
            sc_.iconbitmap('logo.ico')
        except:
            try:
                urlretrieve("https://gitee.com/NewbieXvwu/MinecraftSkinDownloader/raw/main/logo.ico", "logo.ico")
                sc_.iconbitmap('logo.ico')
            except:
                urlretrieve("https://github.com/NewbieXvwu/MinecraftSkinDownloader/raw/main/logo.ico", "logo.ico")
                sc_.iconbitmap('logo.ico')

        # 高DPI适配（仅Windows系统）
        try:
            ctypes.windll.shcore.SetProcessDpiAwareness(1)
            ScaleFactor = ctypes.windll.shcore.GetScaleFactorForDevice(0)
            sc_.tk.call('tk', 'scaling', ScaleFactor / 75)
        except:
            pass

        # 进度条动画效果
        def showmain():
            while True:
                # 进度条递增动画
                for i in range(100):
                    try:
                        pb_['value'] = i + 1
                        sc_.update()
                        time.sleep(0.05)
                    except:
                        exit()
                # 进度条递减动画
                for i in range(100):
                    try:
                        pb_['value'] = 100 - i
                        sc_.update()
                        time.sleep(0.05)
                    except:
                        exit()

        # 线程停止控制（安装完成后终止进程）
        def ThreadStop():
            while True:
                try:
                    if ThreadShouldStop:
                        # 强制终止Python相关进程
                        try:
                            os.system("taskkill -f -im python.exe")
                            os.system("taskkill -f -im pythonw.exe")
                            os.system("taskkill -f -im py.exe")
                            os.system("taskkill -f -im pyw.exe")
                            os.system("taskkill -f -im python3.exe")
                            os.system("taskkill -f -im pythonw3.exe")
                            os.system("taskkill -f -im py3.exe")
                            os.system("taskkill -f -im pyw3.exe")
                        except:
                            exit()
                except:
                    pass
                time.sleep(0.1)

        # 启动线程停止监控
        run___ = threading.Thread(target=ThreadStop)
        run___.daemon = True
        run___.start()

        # 启动进度条动画线程
        def show():
            run__ = threading.Thread(target=showmain)
            run__.start()

        # 界面元素布局
        lb1_ = Label(sc_, text="正在安装程序必要的运行库……", font=("宋体", 13))
        lb1_.place(x=30, y=30)
        lb2_ = Label(sc_, text="  正在安装：requests\n\n安装完毕后请手动重启程序", font=("宋体", 10))
        lb2_.place(x=150, y=90, anchor="center")
        pb_ = Progressbar(sc_, length=240, mode='indeterminate', orient=tkinter.HORIZONTAL)
        pb_.place(x=30, y=130)

        show()
        sc_.protocol('WM_DELETE_WINDOW', on_closing)  # 绑定窗口关闭事件
        sc_.mainloop()

    # 启动安装进度窗口线程
    run___ = threading.Thread(target=sc_main_)
    run___.start()

    # 执行pip安装requests（使用清华镜像源）
    result = os.popen("pip install -i https://pypi.tuna.tsinghua.edu.cn/simple requests").read()
    if "Successfully installed" in result:  # 安装成功
        import requests
        from requests import delete
        ThreadShouldStop = True  # 触发线程停止
    else:  # 安装失败
        if tkinter.messagebox.askyesno("错误", "运行库安装失败，程序无法继续运行！\n请把以下内容提交给开发者：\n" + result + "\n是否要提交错误？"):
            os.startfile("https://github.com/NewbieXvwu/MinecraftSkinDownloader/issues/new?assignees=&labels=bug&template=bug_report.yml&title=%5B%E6%BC%8F%E6%B4%9E%5D+%E6%97%A0%E6%B3%95%E5%AE%89%E8%A3%85%E4%BE%9D%E8%B5%96%E5%BA%93")
        exit()

    # 尝试安装ttkbootstrap
    try:
        import ttkbootstrap as ttk
    except:  # 未安装ttkbootstrap时执行
        result = os.popen("pip install -i https://pypi.tuna.tsinghua.edu.cn/simple ttkbootstrap").read()
        if "Successfully installed" in result:  # 安装成功
            import ttkbootstrap as ttk
            ThreadShouldStop = True
        else:  # 安装失败
            if tkinter.messagebox.askyesno("错误", "运行库安装失败，程序无法继续运行！\n请把以下内容提交给开发者：\n" + result + "\n是否要提交错误？"):
                os.startfile("https://github.com/NewbieXvwu/MinecraftSkinDownloader/issues/new?assignees=&labels=bug&template=bug_report.yml&title=%5B%E6%BC%8F%E6%B4%9E%5D+%E6%97%A0%E6%B3%95%E5%AE%89%E8%A3%85%E4%BE%9D%E8%B5%96%E5%BA%93")
            exit()

else:  # 已安装requests时，检查ttkbootstrap
    try:
        import ttkbootstrap as ttk
    except:  # 未安装ttkbootstrap时执行
        from tkinter import *
        import tkinter
        from tkinter.ttk import *

        # 显示安装进度窗口（与前面逻辑类似，此处为安装ttkbootstrap）
        def sc_main_():
            sc_ = Tk()
            # 窗口居中设置
            scw = sc_.winfo_screenwidth()
            sch = sc_.winfo_screenheight()
            w = 300
            h = 200
            x = (scw - w) / 2
            y = (sch - h) / 2
            sc_.title("正在安装运行库")
            sc_.geometry("%dx%d+%d+%d" % (w, h, x, y))
            sc_.maxsize(w, h)
            sc_.minsize(w, h)

            # 尝试设置窗口图标
            try:
                sc_.iconbitmap('logo.ico')
            except:
                try:
                    urlretrieve("https://gitee.com/NewbieXvwu/MinecraftSkinDownloader/raw/main/logo.ico", "logo.ico")
                    sc_.iconbitmap('logo.ico')
                except:
                    try:
                        urlretrieve("https://github.com/NewbieXvwu/MinecraftSkinDownloader/raw/main/logo.ico", "logo.ico")
                        sc_.iconbitmap('logo.ico')
                    except:
                        pass

            # 高DPI适配
            try:
                ctypes.windll.shcore.SetProcessDpiAwareness(1)
                ScaleFactor = ctypes.windll.shcore.GetScaleFactorForDevice(0)
                sc_.tk.call('tk', 'scaling', ScaleFactor / 75)
            except:
                pass

            # 进度条动画
            def showmain():
                while True:
                    for i in range(100):
                        try:
                            pb_['value'] = i + 1
                            sc_.update()
                            time.sleep(0.05)
                        except:
                            exit()
                    for i in range(100):
                        try:
                            pb_['value'] = 100 - i
                            sc_.update()
                            time.sleep(0.05)
                        except:
                            exit()

            # 线程停止控制
            def ThreadStop():
                while True:
                    try:
                        if ThreadShouldStop:
                            try:
                                os.system("taskkill -f -im python.exe")
                                os.system("taskkill -f -im pythonw.exe")
                                os.system("taskkill -f -im py.exe")
                                os.system("taskkill -f -im pyw.exe")
                                os.system("taskkill -f -im python3.exe")
                                os.system("taskkill -f -im pythonw3.exe")
                                os.system("taskkill -f -im py3.exe")
                                os.system("taskkill -f -im pyw3.exe")
                            except:
                                exit()
                    except:
                        pass
                    time.sleep(0.1)

            run___ = threading.Thread(target=ThreadStop)
            run___.daemon = True
            run___.start()

            def show():
                run__ = threading.Thread(target=showmain)
                run__.start()

            # 界面元素布局
            lb1_ = Label(sc_, text="正在安装程序必要的运行库……", font=("宋体", 13))
            lb1_.place(x=30, y=30)
            lb2_ = Label(sc_, text=" 正在安装：ttkbootstrap\n\n安装完毕后请手动重启程序", font=("宋体", 10))
            lb2_.place(x=150, y=90, anchor="center")
            pb_ = Progressbar(sc_, length=240, mode='indeterminate', orient=tkinter.HORIZONTAL)
            pb_.place(x=30, y=130)

            show()
            sc_.protocol('WM_DELETE_WINDOW', on_closing)
            sc_.mainloop()

        # 启动安装窗口线程
        run___ = threading.Thread(target=sc_main_)
        run___.daemon = True
        run___.start()

        # 执行pip安装ttkbootstrap
        result = os.popen("pip install -i https://pypi.tuna.tsinghua.edu.cn/simple ttkbootstrap").read()
        if "Successfully installed" in result:  # 安装成功
            from ttkbootstrap import Style
            ThreadShouldStop = True
        else:  # 安装失败
            if tkinter.messagebox.askyesno("错误", "运行库安装失败，程序无法继续运行！\n请把以下内容提交给开发者：\n" + result + "\n是否要提交错误？"):
                os.startfile("https://github.com/NewbieXvwu/MinecraftSkinDownloader/issues/new?assignees=&labels=bug&template=bug_report.yml&title=%5B%E6%BC%8F%E6%B4%9E%5D+%E6%97%A0%E6%B3%95%E5%AE%89%E8%A3%85%E4%BE%9D%E8%B5%96%E5%BA%93")
            exit()

# 输入框验证（实时检查输入状态）
def validate_number():
    while True:
        try:
            if e.get() == "":
                e.config(bootstyle="warning")  # 空输入时警告样式
            else:
                e.config(bootstyle="primary")  # 有输入时正常样式
            time.sleep(0.1)
        except:
            exit()

# 主功能函数：下载皮肤和披风，生成材质包
def getzbmain():
    id_ = e.get()
    if id_ == "":  # 检查输入为空
        tkinter.messagebox.showerror(title='错误', message='请填写内容！')
        e.focus_set()
        e.config(bootstyle="warning")
        return

    # 步骤1：获取玩家UUID
    zt.set("状态：正在向Mojang请求玩家的UUID……")
    url1 = "https://api.mojang.com/users/profiles/minecraft/" + id_
    r = requests.get(url1)
    del url1  # 释放变量
    if not r.status_code == 200:  # 检查请求状态
        zt.set("状态：Bugjump出现错误，请检查你的输入！")
        return
    zt.set("状态：读取UUID中……")
    try:
        uuid = json.loads(r.text)['id']  # 解析UUID
    except:
        zt.set("状态：Bugjump出现错误，请检查你的输入！")
        return
    del r

    # 步骤2：获取皮肤下载地址
    zt.set("状态：向Mojang请求下载皮肤的地址中……")
    url2 = "https://sessionserver.mojang.com/session/minecraft/profile/" + uuid
    r = requests.get(url2)
    del url2
    if not r.status_code == 200:
        zt.set("状态：Bugjump出现错误，请检查你的输入！")
        return
    zt.set("状态：读取皮肤下载地址中……")
    try:
        properties = json.loads(r.text)["properties"][0]["value"]  # 获取base64编码的属性
    except:
        zt.set("状态：解析皮肤地址失败！")
        return
    del r

    # 步骤3：解码并提取皮肤/披风URL
    zt.set("状态：解码皮肤下载地址中……")
    properties = base64.b64decode(properties).decode()  # base64解码
    textures = json.loads(properties)["textures"]  # 提取纹理信息
    del properties

    # 下载披风（如果存在）
    havecape = False
    try:
        cape_url = textures["CAPE"]["url"]
        filename = id_ + '_cape.png'
        zt.set("状态：成功获取披风下载直链，正在尝试下载……")
        urlretrieve(cape_url, filename)
        havecape = True
    except:
        pass  # 无披风时忽略

    # 下载皮肤
    try:
        skin_info = textures["SKIN"]
        skin_url = skin_info["url"]
        # 检查皮肤模型类型（alex/steve）
        isalex = skin_info.get("metadata", {}).get("model", "")
    except:
        zt.set("状态：获取皮肤信息失败！")
        return

    filename = id_ + '.png'
    zt.set("状态：成功获取皮肤下载直链，正在尝试下载……")
    urlretrieve(skin_url, filename)
    del skin_url, filename

    # 下载完成提示
    if havecape:
        tkinter.messagebox.showwarning(title="下载完毕", message="下载完毕！此玩家还拥有披风，已同时下载！")
    else:
        tkinter.messagebox.showwarning(title="下载完毕", message="下载完毕！")
    zt.set("状态：待命")
    lb2.config(textvariable=zt)

    # 非Linux系统支持生成材质包
    if not isLinux():
        exit_ = tkinter.messagebox.askyesno(title="下载完毕", message="下载完毕！按“确认”打包皮肤成材质包，或者按“取消”打开文件！")
        if exit_:  # 生成材质包
            # 清理旧临时目录
            try:
                shutil.rmtree(f"./{id_}")
            except:
                zt.set("状态：正在删除旧的临时目录……")

            # 创建临时目录结构
            zt.set("状态：正在创建新的临时目录……")
            os.mkdir(f"./{id_}")

            # 创建材质包说明文件
            zt.set("状态：正在创建材质包说明文件……")
            with open(f'./{id_}/pack.mcmeta', 'w') as f:
                f.write("{\"pack\":{\"pack_format\":4,\"description\":\"§cSkin Resourcepack\"}}")

            # 下载材质包Logo
            zt.set("状态：正在下载材质包Logo……")
            urlretrieve("https://pic.downk.cc/item/5ff174673ffa7d37b35bb165.png", f"./{id_}/pack.png")

            # 创建皮肤存放目录结构
            zt.set("状态：正在创建皮肤目录……")
            os.makedirs(f"./{id_}/assets/minecraft/textures/entity", exist_ok=True)

            # 复制皮肤文件（根据模型类型）
            zt.set("状态：正在复制皮肤文件……")
            if isalex == "slim":
                os.system(f"copy {id_}.png .\\{id_}\\assets\\minecraft\\textures\\entity\\alex.png")
            else:
                os.system(f"copy {id_}.png .\\{id_}\\assets\\minecraft\\textures\\entity\\steve.png")

            # 压缩为材质包
            zt.set("状态：正在压缩材质包……")
            shutil.make_archive(f"Skin_{id_}", 'zip', id_)

            # 清理临时目录
            zt.set("状态：正在删除临时目录……")
            shutil.rmtree(f"./{id_}")
            zt.set("状态：待命")

            # 检测Minecraft目录并提示导入
            if os.path.exists(".\\.minecraft\\resourcepacks"):
                exit_ = tkinter.messagebox.askyesno(title="创建材质包成功", message="成功创建材质包！\n注意：材质包会替换所有玩家皮肤！\n检测到Minecraft安装，是否直接导入？")
                if exit_:
                    os.system(f"copy Skin_{id_}.zip .\\.minecraft\\resourcepacks\\{id_}.zip")
                    if tkinter.messagebox.askyesno(title="导入成功", message="导入成功！\n是否要打开材质包文件夹？"):
                        os.system("start \"\" .\\.minecraft\\resourcepacks\\")
                else:
                    os.system(f"start \"\" \"Skin_{id_}.zip\"")
            else:
                if tkinter.messagebox.askyesno(title="创建材质包成功", message="创建材质包成功！是否打开文件？"):
                    os.system(f"start \"\" \"Skin_{id_}.zip\"")
        else:  # 直接打开皮肤文件
            try:
                os.startfile(f"{id_}.png")
            except:
                pass

# 多线程调用主功能（防止GUI卡死）
def getzb(ev=None):
    run_ = threading.Thread(target=getzbmain)
    run_.start()

# 关于窗口功能
def info():
    # 打开相关链接的辅助函数
    def opengithub():
        os.startfile("https://github.com/NewbieXvwu/MinecraftSkinDownloader")
    def opengitee():
        os.startfile("https://gitee.com/NewbieXvwu/MinecraftSkinDownloader")
    def openbilibili():
        os.startfile("https://space.bilibili.com/505201154")

    # 创建关于窗口
    about = Toplevel()
    about.title("关于本程序")
    # 窗口居中
    aboutscw = about.winfo_screenwidth()
    aboutsch = about.winfo_screenheight()
    aboutw = 300
    abouth = 210
    aboutx = (aboutscw - aboutw) / 2
    abouty = (aboutsch - abouth) / 2
    about.geometry("%dx%d+%d+%d" % (aboutw, abouth, aboutx, abouty))
    # 设置图标
    try:
        about.iconbitmap('logo.ico')
    except:
        pass
    about.resizable(width=False, height=False)  # 禁止调整大小

    # 界面元素
    lb4 = Label(about, text="关于本程序", font=("宋体", 15))
    lb4.place(x=100, y=30)
    # 根据系统调整文本布局
    if isLinux():
        lb5 = Label(about, text="   这是一个可以简单地下载任何\n Minecraft正版玩家皮肤的软件。", font=("宋体", 14))
    else:
        lb5 = Label(about, text=" 这是一个可以简单地下载任何\n\n Minecraft正版玩家皮肤的软件。", font=("宋体", 14))
    lb5.place(x=150, y=100, anchor=CENTER)
    # 链接按钮
    btn3 = Button(about, text="Github", command=opengithub, bootstyle="link")
    btn3.place(x=220, y=155)
    btn4 = Button(about, text="Gitee", command=opengitee, bootstyle="link")
    btn4.place(x=122.5, y=155)
    btn5 = Button(about, text="Bilibili", command=openbilibili, bootstyle="link")
    btn5.place(x=25, y=155)
    about.lift()  # 窗口置顶

# 主窗口初始化
from tkinter import *
import ttkbootstrap as ttk
from ttkbootstrap.constants import *
from tkinter.ttk import *

sc = Tk()
# 窗口居中
scw = sc.winfo_screenwidth()
sch = sc.winfo_screenheight()
w = 500
h = 300
x = (scw - w) / 2
y = (sch - h) / 2
sc.title(f"Minecraft正版皮肤下载器{version} By 萌新欻無")
sc.geometry("%dx%d+%d+%d" % (w, h, x, y))
sc.maxsize(w, h)
sc.minsize(w, h)
sc.resizable(width=False, height=False)  # 禁止调整大小

# 设置窗口图标
try:
    sc.iconbitmap('logo.ico')
except:
    try:
        urlretrieve("https://gitee.com/NewbieXvwu/MinecraftSkinDownloader/raw/main/logo.ico", "logo.ico")
        sc.iconbitmap('logo.ico')
    except:
        try:
            urlretrieve("https://github.com/NewbieXvwu/MinecraftSkinDownloader/raw/main/logo.ico", "logo.ico")
            sc.iconbitmap('logo.ico')
        except:
            pass

# 高DPI适配
try:
    ctypes.windll.shcore.SetProcessDpiAwareness(1)
    ScaleFactor = ctypes.windll.shcore.GetScaleFactorForDevice(0)
    sc.tk.call('tk', 'scaling', ScaleFactor / 75)
except:
    pass

# 主界面元素
lb1 = Label(sc, text="请输入Minecraft正版账号名称", font=("宋体", 15))
lb1.place(x=110, y=50)

e = Entry(sc, width=20)
# 根据系统调整输入框位置
if isLinux():
    e.place(x=160, y=120)
else:
    e.place(x=170, y=120)
e.bind("<Return>", getzb)  # 绑定回车键触发下载
e.focus_set()  # 初始焦点

# 启动输入验证线程
run____ = threading.Thread(target=validate_number)
run____.start()

# 下载按钮
btn1 = Button(sc, text="点击获取", command=getzb, bootstyle=(SUCCESS, OUTLINE))
btn1.place(x=210, y=190)

# 状态显示
zt = tkinter.StringVar()
zt.set("状态：待命")
lb2 = Label(sc, textvariable=zt, font=("宋体", 15))
# 根据系统调整状态标签位置
if isLinux():
    lb2.place(x=10, y=260)
else:
    lb2.place(x=10, y=270)

# 关于按钮
btn2 = Button(sc, text="关于", command=info, bootstyle=(SUCCESS, OUTLINE))
if isLinux():
    btn2.place(x=440, y=255)
else:
    btn2.place(x=440, y=260)

# 版本标签
lb3 = Label(sc, text=version, font=("宋体", 10))
lb3.place(x=5, y=5)

# 主题切换菜单
cmb = Menubutton(sc, width=7, bootstyle="info-outline")
cmb.place(x=390, y=5)

# 主题切换函数
def light():
    cmb.config(text="浅色模式")
    style = ttk.Style("cosmo")
def dark():
    cmb.config(text="深色模式")
    style = ttk.Style("superhero")

# 创建主题菜单
filemenu = tkinter.Menu(cmb, tearoff=False)
filemenu.add_command(label="浅色模式", command=light)
filemenu.add_command(label="深色模式", command=dark)
cmb.config(menu=filemenu)

# 尝试读取Windows系统主题并自动适配
try:
    key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, r"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize")
    i = 0
    while True:
        name, value, type = winreg.EnumValue(key, i)
        if str(name) == "AppsUseLightTheme":
            break
        i += 1
    # 根据系统主题设置初始模式
    if value == 0:
        light()
    else:
        dark()
except:
    pass

# 检查Python版本与系统兼容性
try:
    # 判断Windows版本和Python版本是否存在兼容性问题
    if float(f"{platform.version().split('.')[0]}.{platform.version().split('.')[1]}") > 6.3 and int(platform.python_version().split(".")[1]) <= 8:
        if tkinter.messagebox.askyesno(title="您正在使用过旧的Python", message=f"您的操作系统为Windows {platform.version().split('.')[0]}，\n但本程序正运行在版本为{platform.python_version()}的Python上。\n建议下载更稳定的版本，是否前往下载？"):
            os.startfile("https://github.com/NewbieXvwu/MinecraftSkinDownloader/releases")
            exit()
except:
    pass

# 预览版警告
if ispreview:
    if not tkinter.messagebox.askyesno(title="您正在使用预览版", message=f"您正在使用的版本为{version}，这是一个预览版。\n使用预览版可能会带来一些不可预知的问题！\n您是否要继续？"):
        os.startfile("https://github.com/NewbieXvwu/MinecraftSkinDownloader/releases")
        exit()

# 启动主窗口事件循环
sc.mainloop()